home *** CD-ROM | disk | FTP | other *** search
/ SPACE 2 / SPACE - Library 2 - Volume 1.iso / program / 515 / rcs5ap1s.lzh / RCSUTIL.C < prev    next >
C/C++ Source or Header  |  1991-01-30  |  20KB  |  898 lines

  1. /*
  2.  *                     RCS utilities
  3.  */
  4.  
  5. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  6.    Copyright 1990 by Paul Eggert
  7.    Distributed under license by the Free Software Foundation, Inc.
  8.  
  9. This file is part of RCS.
  10.  
  11. RCS is free software; you can redistribute it and/or modify
  12. it under the terms of the GNU General Public License as published by
  13. the Free Software Foundation; either version 1, or (at your option)
  14. any later version.
  15.  
  16. RCS is distributed in the hope that it will be useful,
  17. but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19. GNU General Public License for more details.
  20.  
  21. You should have received a copy of the GNU General Public License
  22. along with RCS; see the file COPYING.  If not, write to
  23. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  24.  
  25. Report problems and direct all questions to:
  26.  
  27.     rcs-bugs@cs.purdue.edu
  28.  
  29. */
  30.  
  31.  
  32.  
  33.  
  34. /* $Log: rcsutil.c,v $
  35.  * Revision 5.8  1991/01/30  14:21:32  apratt
  36.  * CI with RCS version 5
  37.  *
  38.  * Revision 5.7  91/01/29  17:46:04  apratt
  39.  * Added quotes around args with spaces in call to system
  40.  * 
  41.  * Revision 5.6  91/01/11  12:46:44  apratt
  42.  * First version that compiles.
  43.  * 
  44.  * Revision 5.5  90/12/04  05:18:49  eggert
  45.  * checked in with -k by apratt at 91.01.10.13.15.32.
  46.  * 
  47.  * Revision 5.5  1990/12/04  05:18:49  eggert
  48.  * Don't output a blank line after a signal diagnostic.
  49.  * Use -I for prompts and -q for diagnostics.
  50.  *
  51.  * Revision 5.4  1990/11/01  05:03:53  eggert
  52.  * Remove unneeded setid check.  Add awrite(), fremember().
  53.  *
  54.  * Revision 5.3  1990/10/06  00:16:45  eggert
  55.  * Don't fread F if feof(F).
  56.  *
  57.  * Revision 5.2  1990/09/04  08:02:31  eggert
  58.  * Store fread()'s result in an fread_type object.
  59.  *
  60.  * Revision 5.1  1990/08/29  07:14:07  eggert
  61.  * Declare getpwuid() more carefully.
  62.  *
  63.  * Revision 5.0  1990/08/22  08:13:46  eggert
  64.  * Add setuid support.  Permit multiple locks per user.
  65.  * Remove compile-time limits; use malloc instead.
  66.  * Switch to GMT.  Permit dates past 1999/12/31.
  67.  * Add -V.  Remove snooping.  Ansify and Posixate.
  68.  * Tune.  Some USG hosts define NSIG but not sys_siglist.
  69.  * Don't run /bin/sh if it's hopeless.
  70.  * Don't leave garbage behind if the output is an empty pipe.
  71.  * Clean up after SIGXCPU or SIGXFSZ.  Print name of signal that caused cleanup.
  72.  *
  73.  * Revision 4.6  89/05/01  15:13:40  narten
  74.  * changed copyright header to reflect current distribution rules
  75.  * 
  76.  * Revision 4.5  88/11/08  16:01:02  narten
  77.  * corrected use of varargs routines
  78.  * 
  79.  * Revision 4.4  88/08/09  19:13:24  eggert
  80.  * Check for memory exhaustion.
  81.  * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch.
  82.  * Use execv(), not system(); yield exit status like diff(1)'s.
  83.  * 
  84.  * Revision 4.3  87/10/18  10:40:22  narten
  85.  * Updating version numbers. Changes relative to 1.1 actually
  86.  * relative to 4.1
  87.  * 
  88.  * Revision 1.3  87/09/24  14:01:01  narten
  89.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  90.  * warnings)
  91.  * 
  92.  * Revision 1.2  87/03/27  14:22:43  jenkins
  93.  * Port to suns
  94.  * 
  95.  * Revision 4.1  83/05/10  15:53:13  wft
  96.  * Added getcaller() and findlock().
  97.  * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal
  98.  * (needed for background jobs in older shells). Added restoreints().
  99.  * Removed printing of full RCS path from logcommand().
  100.  * 
  101.  * Revision 3.8  83/02/15  15:41:49  wft
  102.  * Added routine fastcopy() to copy remainder of a file in blocks.
  103.  *
  104.  * Revision 3.7  82/12/24  15:25:19  wft
  105.  * added catchints(), ignoreints() for catching and ingnoring interrupts;
  106.  * fixed catchsig().
  107.  *
  108.  * Revision 3.6  82/12/08  21:52:05  wft
  109.  * Using DATEFORM to format dates.
  110.  *
  111.  * Revision 3.5  82/12/04  18:20:49  wft
  112.  * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
  113.  * lockedby-field.
  114.  *
  115.  * Revision 3.4  82/12/03  17:17:43  wft
  116.  * Added check to addlock() ensuring only one lock per person.
  117.  * Addlock also returns a pointer to the lock created. Deleted fancydate().
  118.  *
  119.  * Revision 3.3  82/11/27  12:24:37  wft
  120.  * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c.
  121.  * Introduced macro SNOOP so that snoop can be placed in directory other than
  122.  * TARGETDIR. Changed %02d to %.2d for compatibility reasons.
  123.  *
  124.  * Revision 3.2  82/10/18  21:15:11  wft
  125.  * added function getfullRCSname().
  126.  *
  127.  * Revision 3.1  82/10/13  16:17:37  wft
  128.  * Cleanup message is now suppressed in quiet mode.
  129.  */
  130.  
  131.  
  132.  
  133.  
  134. #include "rcsbase.h"
  135.  
  136. #if !MAKEDEPEND && defined(declare_getpwuid)
  137. #    include <pwd.h>
  138.     declare_getpwuid
  139. #endif
  140.  
  141. libId(utilId, "$Id: rcsutil.c,v 5.8 1991/01/30 14:21:32 apratt Exp $")
  142.  
  143. #if lint
  144.     malloc_type lintalloc;
  145. #endif
  146.  
  147. #if has_getuid
  148.     uid_t ruid;
  149. #endif
  150. #if SETID
  151.     static uid_t euid;
  152.     static gid_t egid, rgid;
  153. #endif
  154.  
  155. /*
  156.  * list of blocks allocated with ftestalloc()
  157.  * These blocks can be freed by ffree when we're done with the current file.
  158.  * We could put the free block inside struct alloclist, rather than a pointer
  159.  * to the free block, but that would be less portable.
  160.  */
  161. struct alloclist {
  162.     malloc_type alloc;
  163.     struct alloclist *nextalloc;
  164. };
  165. static struct alloclist *alloced;
  166.  
  167.  
  168.     static malloc_type
  169. okalloc(p)
  170.     malloc_type p;
  171. {
  172.     if (!p)
  173.         faterror("out of memory");
  174.     return p;
  175. }
  176.  
  177.     malloc_type
  178. testalloc(size)
  179.     size_t size;
  180. /* Allocate a block, testing that the allocation succeeded.  */
  181. {
  182.     return okalloc(malloc(size));
  183. }
  184.  
  185.     malloc_type
  186. testrealloc(ptr, size)
  187.     malloc_type ptr;
  188.     size_t size;
  189. /* Reallocate a block, testing that the allocation succeeded.  */
  190. {
  191.     return okalloc(realloc(ptr, size));
  192. }
  193.  
  194.     malloc_type
  195. fremember(ptr)
  196.     malloc_type ptr;
  197. /* Remember PTR in 'alloced' so that it can be freed later.  Yield PTR.  */
  198. {
  199.     register struct alloclist *q = talloc(struct alloclist);
  200.     q->nextalloc = alloced;
  201.     alloced = q;
  202.     return q->alloc = ptr;
  203. }
  204.  
  205.     malloc_type
  206. ftestalloc(size)
  207.     size_t size;
  208. /* Allocate a block, putting it in 'alloced' so it can be freed later. */
  209. {
  210.     return fremember(testalloc(size));
  211. }
  212.  
  213.     void
  214. ffree()
  215. /* Free all blocks allocated with ftestalloc().  */
  216. {
  217.     register struct alloclist *p, *q;
  218.     for (p = alloced;  p;  p = q) {
  219.         q = p->nextalloc;
  220.         tfree(p->alloc);
  221.         tfree(p);
  222.     }
  223.     alloced = nil;
  224. }
  225.  
  226.     void
  227. ffree1(f)
  228.     register const char *f;
  229. /* Free the block f, which was allocated by ftestalloc.  */
  230. {
  231.     register struct alloclist *p, **a = &alloced;
  232.  
  233.     while ((p = *a)->alloc  !=  f)
  234.         a = &p->nextalloc;
  235.     *a = p->nextalloc;
  236.     tfree(p->alloc);
  237.     tfree(p);
  238. }
  239.  
  240.     const char *
  241. strsave(s)
  242.     const char *s;
  243. /* Save s in permanently allocated storage. */
  244. {
  245.     return strcpy(tnalloc(char, strlen(s)+1), s);
  246. }
  247.  
  248.     const char *
  249. fstrsave(s)
  250.     const char *s;
  251. /* Save s in storage that will be deallocated when we're done with this file. */
  252. {
  253.     return strcpy(ftnalloc(char, strlen(s)+1), s);
  254. }
  255.  
  256.     char *
  257. cgetenv(name)
  258.     const char *name;
  259. /* Like getenv(), but yield a copy; getenv() can overwrite old results. */
  260. {
  261.     register char *p;
  262.  
  263.     return (p=getenv(name)) ? strsave(p) : p;
  264. }
  265.  
  266.  
  267.     const char *
  268. getcaller()
  269. /* Function: gets the caller's login.
  270.  */
  271. {
  272.     static char *name;
  273.  
  274.     if (!name) {
  275.         if (!(
  276.             /* Use getenv() if we're trustworthy; it's much faster.  */
  277. #if SETID
  278.             euid==ruid && egid==rgid &&
  279. #endif
  280.             (
  281.                 (name = cgetenv("LOGNAME"))
  282.             ||    (name = cgetenv("USER"))
  283.             )
  284.  
  285.             /* Follow a procedure recommended by Posix 1003.1-1988.  */
  286.             ||    (name = getlogin())
  287.         )) {
  288. #if has_getuid & defined(declare_getpwuid)
  289.             const struct passwd *pw = getpwuid(ruid);
  290.             if (!pw)
  291.                 faterror("no password entry for userid %lu",
  292.                      (unsigned long)ruid
  293.                 );
  294.             name = pw->pw_name;
  295. #else
  296.             faterror("Who are you?  Please set LOGNAME.");
  297. #endif
  298.         }
  299.         checksid(name);
  300.     }
  301.     return name;
  302. }
  303.  
  304.  
  305.  
  306.     int
  307. findlock(delete, target)
  308.     int delete;
  309.     struct hshentry **target;
  310. /* Finds the first lock held by caller and returns a pointer
  311.  * to the locked delta; also removes the lock if delete is set.
  312.  * Returns 0 for no locks, 1 for one, 2 for two or more.
  313.  * If one lock, puts it into *target.
  314.  */
  315. {
  316.     register struct lock *next, **trail, **found = nil;
  317.  
  318.     for (trail = &Locks;  (next = *trail);  trail = &next->nextlock)
  319.         if (strcmp(getcaller(), next->login)  ==  0) {
  320.             if (found) {
  321.                 error("multiple revisions locked by %s; please specify one", getcaller());
  322.                 return 2;
  323.             }
  324.             found = trail;
  325.         }
  326.     if (!found)
  327.         return 0;
  328.     next = *found;
  329.     *target = next->delta;
  330.     if (delete) {
  331.         next->delta->lockedby = nil;
  332.         *found = next->nextlock;
  333.     }
  334.     return 1;
  335. }
  336.  
  337.  
  338.  
  339.  
  340.  
  341.  
  342.  
  343.     int
  344. addlock(delta)
  345. struct hshentry * delta;
  346. /* Add a lock held by caller to delta and yield 1 if successful.
  347.  * Print an error message and yield -1 if no lock is added because
  348.  * the delta is locked by somebody other than caller.
  349.  * Yield 0 if the caller already holds the lock.  */
  350. {
  351.         struct lock * next;
  352.  
  353.         next=Locks;
  354.         while (next!=nil) {
  355.                 if (cmpnum(delta->num,next->delta->num)==0) {
  356.             if (strcmp(getcaller(),next->login)==0)
  357.                 return 0;
  358.                         else {
  359.                                 error("revision %s already locked by %s",
  360.                                       delta->num, next->login);
  361.                 return -1;
  362.                         }
  363.                 }
  364.         next = next->nextlock;
  365.     }
  366.         /* set up new lockblock */
  367.     next = ftalloc(struct lock);
  368.     delta->lockedby=next->login=getcaller();
  369.         next->delta= delta;
  370.         next->nextlock=Locks;
  371.         Locks=next;
  372.     return 1;
  373. }
  374.  
  375.  
  376.  
  377.     int
  378. addsymbol(num, name, rebind)
  379.     const char *num, *name;
  380.     int rebind;
  381. /* Function: adds a new symbolic name and associates it with revision num.
  382.  * If name already exists and rebind is true, the name is associated
  383.  * with the new num; otherwise, an error message is printed and
  384.  * false returned. Returns true it successful.
  385.  */
  386. {       register struct assoc * next;
  387.         next=Symbols;
  388.         while (next!=nil) {
  389.                 if (strcmp(name,next->symbol)==0) {
  390.                         if (rebind) {
  391.                 next->num = num;
  392.                                 return true;
  393.                         } else {
  394.                                 error("symbolic name %s already bound to %s",
  395.                     name, next->num);
  396.                                 return false;
  397.                         }
  398.                 } else  next = next->nextassoc;
  399.         }
  400.         /* not found; insert new pair. */
  401.     next = ftalloc(struct assoc);
  402.         next->symbol=name;
  403.     next->num = num;
  404.         next->nextassoc=Symbols;
  405.         Symbols = next;
  406.         return true;
  407. }
  408.  
  409.  
  410.  
  411.  
  412. int checkaccesslist()
  413. /* function: Returns true if caller is the superuser, the owner of the
  414.  * file, the access list is empty, or caller is on the access list.
  415.  * Prints an error message and returns false otherwise.
  416.  */
  417. {
  418.     register const struct access *next;
  419.  
  420.     if (!AccessList || strcmp(getcaller(),"root")==0)
  421.                 return true;
  422.  
  423.         next=AccessList;
  424.         do {
  425.         if (strcmp(getcaller(),next->login)==0)
  426.                         return true;
  427.                 next=next->nextaccess;
  428.         } while (next!=nil);
  429.  
  430. #if has_getuid
  431.     {
  432.         struct stat statbuf;
  433.         VOID fstat(fileno(finptr),&statbuf);  /* get owner of file */
  434.         if (myself(statbuf.st_uid)) return true;
  435.     }
  436. #endif
  437.  
  438.     error("user %s not on the access list", getcaller());
  439.         return false;
  440. }
  441.  
  442.  
  443. /*
  444.  *     Signal handling
  445.  *
  446.  * ANSI C places too many restrictions on signal handlers.
  447.  * We obey as many of them as we can.
  448.  * Posix places fewer restrictions, and we are Posix-compatible here.
  449.  */
  450.  
  451. #if DONT_USE_SIGNALS
  452.  
  453. void ignoreints() { ; }        /* stubs for signal-related functions */
  454. void restoreints() { ; }
  455. void catchints() { ; }
  456.  
  457. #else
  458.  
  459. static volatile sig_atomic_t heldsignal, holdlevel;
  460.  
  461.     static signal_type
  462. catchsig(s)
  463.     int s;
  464. {
  465.     const char *sname;
  466.     char buf[BUFSIZ];
  467.  
  468. #if sig_zaps_handler
  469.     /* If a signal arrives before we reset the signal handler, we lose. */
  470.     VOID signal(s, SIG_IGN);
  471. #endif
  472.     if (holdlevel) {
  473.         heldsignal = s;
  474.         return;
  475.     }
  476.     ignoreints();
  477.     setrid();
  478.     if (!quietflag) {
  479.         sname = nil;
  480. #if has_sys_siglist & defined(NSIG)
  481.         if ((unsigned)s < NSIG) {
  482. #        ifndef sys_siglist
  483.             extern const char *sys_siglist[];
  484. #        endif
  485.         sname = sys_siglist[s];
  486.         }
  487. #else
  488.         switch (s) {
  489. #ifdef SIGHUP
  490.         case SIGHUP:    sname = "Hangup";  break;
  491. #endif
  492. #ifdef SIGINT
  493.         case SIGINT:    sname = "Interrupt";  break;
  494. #endif
  495. #ifdef SIGPIPE
  496.         case SIGPIPE:    sname = "Broken pipe";  break;
  497. #endif
  498. #ifdef SIGQUIT
  499.         case SIGQUIT:    sname = "Quit";  break;
  500. #endif
  501. #ifdef SIGTERM
  502.         case SIGTERM:    sname = "Terminated";  break;
  503. #endif
  504. #ifdef SIGXCPU
  505.         case SIGXCPU:    sname = "Cputime limit exceeded";  break;
  506. #endif
  507. #ifdef SIGXFSZ
  508.         case SIGXFSZ:    sname = "Filesize limit exceeded";  break;
  509. #endif
  510.         }
  511. #endif
  512.         if (sname)
  513.         VOID sprintf(buf, "\nRCS: %s.  Cleaning up.\n", sname);
  514.         else
  515.         VOID sprintf(buf, "\nRCS: Signal %d.  Cleaning up.\n", s);
  516.         VOID write(STDERR_FILENO, buf, strlen(buf));
  517.     }
  518.     exiterr();
  519. }
  520.  
  521.     void
  522. ignoreints()
  523. {
  524.     ++holdlevel;
  525. }
  526.  
  527.     void
  528. restoreints()
  529. {
  530.     if (!--holdlevel && heldsignal)
  531.         VOID catchsig(heldsignal);
  532. }
  533.  
  534. static const sig[] = {
  535. #ifdef SIGHUP
  536.     SIGHUP,
  537. #endif
  538. #ifdef SIGINT
  539.     SIGINT,
  540. #endif
  541. #ifdef SIGPIPE
  542.     SIGPIPE,
  543. #endif
  544. #ifdef SIGQUIT
  545.     SIGQUIT,
  546. #endif
  547. #ifdef SIGTERM
  548.     SIGTERM,
  549. #endif
  550. #ifdef SIGXCPU
  551.     SIGXCPU,
  552. #endif
  553. #ifdef SIGXFSZ
  554.     SIGXFSZ,
  555. #endif
  556. };
  557. #define SIGS (sizeof(sig)/sizeof(*sig))
  558.  
  559.  
  560. #if has_sigaction
  561.  
  562.     static void
  563.   checksig(r)
  564.     int r;
  565.   {
  566.     if (r < 0)
  567.         efaterror("signal");
  568.   }
  569.  
  570.     void
  571.   catchints()
  572.   {
  573.     register int i;
  574.     sigset_t blocked;
  575.     struct sigaction act;
  576.  
  577.     checksig(sigemptyset(&blocked));
  578.     for (i=SIGS; 0<=--i; )
  579.         checksig(sigaddset(&blocked, sig[i]));
  580.     for (i=SIGS; 0<=--i; ) {
  581.         checksig(sigaction(sig[i], (struct sigaction*)nil, &act));
  582.         if (act.sa_handler != SIG_IGN) {
  583.             act.sa_handler = catchsig;
  584.             act.sa_mask = blocked;
  585.             checksig(sigaction(sig[i], &act, (struct sigaction*)nil));
  586.         }
  587.     }
  588.   }
  589.  
  590. #else
  591. #if has_sigblock
  592.  
  593.   void catchints()
  594.   {
  595.     register int i;
  596.     int mask;
  597.  
  598.     mask = 0;
  599.     for (i=SIGS; 0<=--i; )
  600.         mask |= sigmask(sig[i]);
  601.     mask = sigblock(mask);
  602.     for (i=SIGS; 0<=--i; )
  603.         if (signal(sig[i], catchsig) == SIG_IGN)
  604.             VOID signal(sig[i], SIG_IGN);
  605.     VOID sigsetmask(mask);
  606.   }
  607.  
  608. #else
  609.  
  610.   void catchints()
  611.   {
  612.     register i;
  613.     for (i=SIGS; 0<=--i; )
  614.         if (signal(sig[i], SIG_IGN) != SIG_IGN)
  615.             VOID signal(sig[i], catchsig);
  616.   }
  617.  
  618. #endif
  619. #endif
  620.  
  621. /* end if DONT_USE_SIGNALS */
  622. #endif 
  623.  
  624.     void
  625. fastcopy(inf,outf)
  626. FILE * inf, * outf;
  627. /* Function: copies the remainder of file inf to outf.
  628.  */
  629. {       char buf[BUFSIZ];
  630.     register fread_type rcount;
  631.  
  632.         /*now read the rest of the file in blocks*/
  633.     while (!feof(inf)  &&  (rcount = fread(buf,sizeof(char),BUFSIZ,inf))) {
  634.         awrite(buf, rcount, outf);
  635.         }
  636. }
  637.  
  638.     void
  639. awrite(buf, chars, f)
  640.     const char *buf;
  641.     fread_type chars;
  642.     FILE *f;
  643. {
  644.     if (fwrite(buf, sizeof(char), chars, f) != chars)
  645.         IOerror();
  646. }
  647.  
  648.  
  649.  
  650.  
  651.  
  652. /*
  653. * Print RCS format date and time in user-readable format.
  654. */
  655.     void
  656. printdate(f, date, separator)
  657.     register FILE *f;
  658.     const char *date, *separator;
  659. {
  660.     register const char *p = date;
  661.  
  662.     while (*p++ != '.')
  663.         ;
  664.     aprintf(f, "%s%.*s/%.2s/%.2s%s%.2s:%.2s:%s",
  665.         date[2]=='.' && VERSION(5)<=RCSversion  ?  "19"  :  "",
  666.         p-date-1, date,
  667.         p, p+3, separator, p+6, p+9, p+12
  668.     );
  669. }
  670.  
  671.  
  672.  
  673.  
  674. #if !DONT_USE_FORK
  675. static int fdreopen(fd, file, flags, mode)
  676.     int fd;
  677.     const char *file;
  678.     int flags;
  679.     mode_t mode;
  680. {
  681.     int newfd;
  682.     VOID close(fd);
  683.     newfd =
  684. #if !open_can_creat
  685.         flags&O_CREAT ? creat(file,mode) :
  686. #endif
  687.         open(file,flags,mode);
  688.     if (newfd < 0  ||  newfd == fd)
  689.         return newfd;
  690.     fd = dup2(newfd, fd);
  691.     VOID close(newfd);
  692.     return fd;
  693. }
  694.  
  695. static void tryopen(fd,file,flags)
  696.     int fd, flags;
  697.     const char *file;
  698. {
  699.     if (file  &&  fdreopen(fd,file,flags,S_IRUSR|S_IWUSR) != fd) {
  700.         VOID write(STDERR_FILENO, file, strlen(file));
  701.         VOID write(STDERR_FILENO, ": can't open\n", 13);
  702.         _exit(EXIT_TROUBLE);
  703.     }
  704. }
  705.  
  706. /*
  707. * Run a command specified by the strings in 'inoutargs'.
  708. * inoutargs[0], if nonnil, is the name of the input file.
  709. * inoutargs[1], if nonnil, is the name of the output file.
  710. * inoutargs[2..] form the command to be run.
  711. */
  712.     int
  713. runv(inoutargs)
  714.     const char **inoutargs;
  715. {
  716.     int pid;
  717.     int wstatus, w;
  718.     register const char **p;
  719.     oflush();
  720.     eflush();
  721.  
  722.     if (!(pid = vfork())) {
  723.         p = inoutargs;
  724.         tryopen(STDIN_FILENO, *p++, O_RDONLY);
  725.         tryopen(STDOUT_FILENO, *p++, O_CREAT|O_TRUNC|O_WRONLY);
  726.         VOID EXECRCS(*p, p);
  727.         if (errno == ENOEXEC) {
  728.             *--p = "/bin/sh";
  729.             VOID execv(*p, p);
  730.         }
  731.         VOID write(STDERR_FILENO, *p, strlen(*p));
  732.         VOID write(STDERR_FILENO, ": not found\n", 12);
  733.         _exit(EXIT_TROUBLE);
  734.     }
  735.     if (pid < 0)
  736.         return pid;
  737.     do {
  738.         if ((w = wait(&wstatus)) < 0)
  739.             return w;
  740.     } while (w != pid);
  741.     return wstatus;
  742. }
  743. #else
  744.     int
  745. runv(inoutargs)
  746.     const char **inoutargs;
  747. {
  748.     char *rargs;
  749.     int rargs_len;
  750.     int rargs_size;
  751.     const char *infile, *outfile;
  752.     int r;
  753.  
  754.     oflush();
  755.     eflush();
  756.  
  757.     infile = *inoutargs++;
  758.     outfile = *inoutargs++;
  759.  
  760.     /* pre-set rargs_len to the length of the redirection part */
  761.     rargs_len = 0;
  762.     if (infile) rargs_len += strlen(infile) + 2;
  763.     if (outfile) rargs_len += strlen(outfile) + 2;
  764.  
  765.     rargs_size = rargs_len + 32;
  766.  
  767.     rargs = testalloc(rargs_size);
  768.     *rargs = '\0';
  769.  
  770.     /* strcat each arg to rargs, realloc'ing more room as necessary */
  771.     /* If an arg contains a space, then wrap it in quotes. */
  772.     while (*inoutargs) {
  773.         if (rargs_len + strlen(*inoutargs) + 4 > rargs_size) {
  774.             rargs_size += strlen(*inoutargs) + 128;
  775.             rargs = testrealloc(rargs,rargs_size);
  776.         }
  777.         if (strchr(*inoutargs,' ') != NULL) {
  778.             rargs_len += strlen(*inoutargs) + 3;
  779.             strcat(rargs,"\"");
  780.             strcat(rargs,*inoutargs++);
  781.             strcat(rargs,"\" ");
  782.         }
  783.         else {
  784.             rargs_len += strlen(*inoutargs) + 1;
  785.             strcat(rargs,*inoutargs++);
  786.             strcat(rargs," ");
  787.         }
  788.     }
  789.     if (infile) {
  790.         strcat(rargs," <");
  791.         strcat(rargs,infile);
  792.     }
  793.     if (outfile) {
  794.         strcat(rargs," >");
  795.         strcat(rargs,outfile);
  796.     }
  797.     r = system(rargs);
  798.     free(rargs);
  799.     return r;
  800. }
  801. #endif
  802.  
  803. #define CARGSMAX 20
  804. /*
  805. * Run a command.
  806. * The first two arguments are the input and output files (if nonnil);
  807. * the rest specify the command and its arguments.
  808. */
  809.     int
  810. #if has_prototypes
  811. run(const char *infile, const char *outfile, ...)
  812. #else
  813.     /*VARARGS2*/
  814. run(infile, outfile, va_alist)
  815.     const char *infile;
  816.     const char *outfile;
  817.     va_dcl
  818. #endif
  819. {
  820.     va_list ap;
  821.     const char *rgargs[CARGSMAX];
  822.     register i = 0;
  823.     rgargs[0] = infile;
  824.     rgargs[1] = outfile;
  825.     vararg_start(ap, outfile);
  826.     for (i = 2;  (rgargs[i++] = va_arg(ap, const char*));  )
  827.         if (CARGSMAX <= i)
  828.             faterror("too many command arguments");
  829.     va_end(ap);
  830.     return runv(rgargs);
  831. }
  832.  
  833.  
  834. int RCSversion;
  835.  
  836.     void
  837. setRCSversion(str)
  838.     const char *str;
  839. {
  840.     static const char *oldversion;
  841.  
  842.     register const char *s = str + 2;
  843.     int v = VERSION_DEFAULT;
  844.  
  845.     if (oldversion)
  846.         redefined('V');
  847.     oldversion = str;
  848.  
  849.     if (*s) {
  850.         v = 0;
  851.         while (isdigit(*s))
  852.             v  =  10*v + *s++ - '0';
  853.         if (*s)
  854.             faterror("%s isn't a number", str);
  855.         if (v < VERSION_MIN  ||  VERSION_MAX < v)
  856.             faterror("%s out of range %d..%d", str, VERSION_MIN, VERSION_MAX);
  857.     }
  858.  
  859.     RCSversion = VERSION(v);
  860. }
  861.  
  862.     void
  863. initid()
  864. {
  865. #if SETID
  866.     egid = getegid();
  867.     euid = geteuid();
  868.     rgid = getgid();
  869. #endif
  870. #if has_getuid
  871.     ruid = getuid();
  872. #endif
  873. #if SETID || !AKP_BUGFIXES
  874.     /* AKP: originally, setrid was called even if !SETID */
  875.     setrid();
  876. #endif
  877. }
  878.  
  879.  
  880. #if SETID
  881.     void
  882. seteid()
  883. /* Become effective user and group.  */
  884. {
  885.     if (euid!=ruid && seteuid(euid)<0  ||  egid!=rgid && setegid(egid)<0)
  886.         efaterror("seteid");
  887. }
  888.  
  889.     void
  890. setrid()
  891. /* Become real user and group.  */
  892. {
  893.     if (euid!=ruid && seteuid(ruid)<0  ||  egid!=rgid && setegid(rgid)<0)
  894.         efaterror("setrid");
  895. }
  896. #endif
  897.  
  898.